import pygame
import queue as qe

import pygame.locals as locals
import random
import my.neural_network as neural_network
import my.aigame_config as config
import my.evolution as evolution
import numpy as np
import os


'''
game()类
'''

pygame.init()


class Cake(object):
    BTM_IMG = pygame.image.load("./res/blank.png")

    WIDTH = BTM_IMG.get_width() - 32  # surface的宽度
    HEIGHT = BTM_IMG.get_height() - 32  # surface的高度

    cakeXVY = [600 - 48, 0 - 64, 3, -4]  # 记录初始位置和初始速度

    def __init__(self, network, cakeXVY):
        self.x = cakeXVY[0]
        self.y = cakeXVY[1]
        self.x_mov = cakeXVY[2]
        self.y_mov = cakeXVY[3]
        self.g = 0.04
        # 点击之后的横纵加速度
        self.y_g = 0
        self.cut = False
        self.collis = False   # 是否与tower碰撞
        self.alive = True
        self.neural_netwoek = network
        self.live = 0
        self.p = 0



    def draw(self, surface):
        surface.blit(Cake.BTM_IMG, (self.x, self.y))

    def update(self):
        self.x -= self.x_mov
        self.y -= self.y_mov
        self.y_mov += self.g
        # 正常移动

        if self.x > Game.SIZE[0]:
            self.x_mov = -self.x_mov
            self.y_mov = -self.y_mov

        # 反向移动
        if self.x < 0 - 16 or self.x > Game.SIZE[0] - 48:
            self.x_mov = -self.x_mov
            self.y_mov = -self.y_mov
        # 加入回合限制，如果限定回合内没有cut，判定死亡
        if self.x == 0 - 16 or self.x == Game.SIZE[0] - 48:
            self.live += 1
        # if self.live > 3:
        #     self.alive = False
        #     self.live = 0
        # 点击下落
        if self.cut:
            # 抛物线
            # -------------------横轴
            # 横轴速度不变
            # -------------------纵轴
            self.y_mov += self.y_g
            self.y_g += -0.01

        if self.y > Game.SIZE[1]:
            self.alive = False

    # 剪短连接线
    def cut_line(self):
        self.cut = True

    def collision(self, caketower):
        if Game.SIZE[0]/2 + 16 >= self.x >= Game.SIZE[0]/2 - 48:
            if self.p == 0 and self.y < Game.SIZE[1] - 32 * 5 - 48:
                self.p = 1
            if self.y >= Game.SIZE[1] - 32 * 5 - 48 and self.p == 1:
                caketower.click = True
                self.collis = True
                self.p = 0
                return True
            else:
                return False
    # 获取数据
    def get_inputs(self, caketower):
        inputs = []
        # 初始化输入数据
        for _ in range(config.network[0]):
            inputs.append(0.0)
        # if caketower.top_l + 32 <= self.x + 16:  # >= caketower.top_l - 32:
        inputs[0] = self.y / Game.SIZE[1]
        if not self.cut:
            inputs[1] = (self.x + Cake.WIDTH) - (Game.SIZE[0]/2 - 16)
            inputs[2] = self.x - ((Game.SIZE[0]/2 + 16) + Cake.WIDTH)
            print(inputs)
        return inputs

    def draw_line(self, surface):
        if not self.cut:
            pygame.draw.line(surface,
                             (122, 122, 255),
                             (self.x + 32, self.y + 14),
                             (Game.SIZE[0] / 2, 0), 2)  # 画辅助线


class CakeManager(object):
    def __init__(self, ai):


        self.ded_num = 0


        self.cakes = []
        self.ai = ai  # ai世代包含多个个体
        self.cakexyv = [0, 0, 0, 0]  # 只记录最后一个砸上的
        # 利用manager 函数生成下一个世代
        network_data_list = self.ai.manager.create_generation()

        for network_data in network_data_list:
            # 创建新的神经网络
            netwoek = neural_network.NeuralNetwork(config.network[0], config.network[1], config.network[2])
            # 将新世代的数据传入神经网络
            netwoek.setNetwork(network_data)
            # 给Cake装上AI
            cake = Cake(netwoek, Cake.cakeXVY)
            # 形成新一代cake
            self.cakes.append(cake)

    def drawCakes(self, surface):
        for cake in self.cakes:
            cake.draw(surface)

    def updateCakes(self, cakes, score, caketower):
        index = len(self.cakes) - 1
        while index >= 0:
            cake = self.cakes[index]
            if cake.alive:
                inputs = cake.get_inputs(cakes)
                ret = cake.neural_netwoek.getResult(inputs)
                print(ret)
                if ret[0] > 0.5:
                    print("cake",index+1,"cut")
                    cake.cut_line()
                    self.cakexyv[0] = cake.x
                    self.cakexyv[1] = cake.y
                    self.cakexyv[2] = cake.x_mov
                    self.cakexyv[3] = cake.y_mov
                cake.update()
            else:
                # 此处收集淘汰个体的数据0000....
                self.ded_num += 1
                print(self.ded_num)
                self.ai.collect_score(cake.neural_netwoek, score)
                self.cakes.remove(cake)
            index -= 1
            # self.renew_cakes()

    def collision(self, caketower):
        for cake in self.cakes:
            cake.collision(caketower)

    def is_all_died(self):
        if len(self.cakes) == 0:
            return True
        return False

    def draw_line(self, surface):
        for cake in self.cakes:
            cake.draw_line(surface)


class CakeTower(object):
    BTM_IMG = pygame.image.load("./res/cake3.png")  # 画布大小64*64

    # TOP_IMG = pygame.transform.rotate(BTM_IMG, 180)

    def __init__(self, surface):
        self.que = qe.Queue()
        # self.cakes = []
        self.index = 5
        self.surface = surface

        self.x = Game.SIZE[0] / 2 - 32
        self.y = Game.SIZE[1] - 16
        self.x_mov = 1
        self.y_mov = 1
        self.click = False
        self.count = 0
        self.score = 0

    # 初始化caketower
    def tower_init(self):
        for i in range(self.index):
            cakexyv = [self.x, self.y - 32 * (i + 1), 0, 0]
            cake = Cake(self.surface, cakexyv)
            self.que.put(cake)

    def draw(self):
        for i in range(self.que.qsize()):
            cak = self.que.get()  # 取出cake用于展示
            cak.draw(self.surface)
            self.que.put(cak)  # 重新放入queue

    def update(self):

        while self.click and self.count < 32:
            self.count += 1
            for i in range(self.que.qsize()):
                cak = self.que.get()  # 取出cake用于展示
                cak.y += 1
                cak.draw(self.surface)
                self.que.put(cak)  # 重新放入queue
            # self.count += 1
            if self.count == 1:
                self.score += 1
                print("score")
                print(self.score)
        else:
            self.click = False
            self.count = 0


class Score(object):
    def __init__(self):
        self.score = 0
        self.all_imgs = []
        for i in range(10):
            img = pygame.image.load("./res/"+str(i)+".png")
            self.all_imgs.append(img)
        self.x = 0
        self.y = 30
        self.imgs = []

    def draw(self, surface):
        pre_width = 0
        for img in self.imgs:
            pre_width = img.get_width()
            surface.blit(img, (self.x, self.y))
            self.x += pre_width


    def update(self):
        self.imgs.clear()
        index = self.SplitScore()
        for i in index:
            self.imgs.append(self.all_imgs[i])
        width = 0
        for img in self.imgs:
            width += img.get_width()
        self.x = Game.SIZE[0]/2-width/2

    def SplitScore(self):
        index_list = []
        i = 1
        score = self.score
        while True:
            ret = score % 10
            index_list.insert(0, ret)
            score = int(self.score / 10 ** i)
            if score == 0:
                break
            i += 1
        return tuple(index_list)  # 打包成列表


# 引用AI神经网络 创建 ai世代，包含多个ai个体
class Evolution_ANN_AI(object):
    def __init__(self):
        # 初始化AI神经网络
        self.manager = evolution.GenerationManager()

    def collect_score(self, network, score):
        # 利用bird个体数据创建的神经网络，和bird的分数，来生成一个基因个体
        genome = evolution.Genome(network.getNetwork(), score)
        # 将这个个体存入第一个世代
        self.manager.add_genome(genome)


class Game(object):
    SIZE = (600, 480)
    FPS = 500/5

    # PIPE_GAP_SIZE = 80 # 管道间隙
    def __init__(self):
        # 不需要重置的属性
        self.surface = pygame.display.set_mode((600, 480))
        self.clock = pygame.time.Clock()
        self.ai = Evolution_ANN_AI()
        self.generation_num = 0
        self.game_init()

    def game_init(self):
        # 游戏开始后需要更新的属性
        # self.cake = Cake(self.surface)
        # self.score = Score()
        self.time = 0
        self.score = 0
        self.gameRunning = True
        self.cakeManager = CakeManager(self.ai)
        self.caketower = CakeTower(self.surface)
        self.caketower.tower_init()  # 对tower 进行初始化
        self.generation_num += 1

        self.time = 0
        self.gameRunning = True

    def control(self):
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                self.stop()

    def draw(self):
        # 绘制背景
        self.surface.fill((169, 169, 160))
        self.cakeManager.draw_line(self.surface)  # 保证效果，先画辅助线
        self.cakeManager.drawCakes(self.surface)
        # self.cake.draw(self.surface)  # 画蛋糕
        self.caketower.draw()  # 画蛋糕塔
        # self.score.draw(self.surface)

    def update(self):
        self.draw_cake()
        self.cakeManager.collision(self.caketower)


        if self.caketower.click:
            i=0
            for cak in self.cakeManager.cakes:
                if cak.collis:
                    i+=1
                    cak.cakeXVY = self.cakeManager.cakexyv
                    print("  =============================================================================num=",i)



        if self.cakeManager.is_all_died():
            self.restart()
        # self.caketower.update()
        self.cakeManager.updateCakes(self.caketower, self.score, self.caketower)
        # self.score  = self.caketower.score
        self.score += 1


    def draw_cake(self):
        self.cake = Cake(self.surface, [300, 466.4600000000002, 0, 0])
        self.cake.draw(self.surface)


    '''
    start
    '''

    def start(self):

        while self.gameRunning:
            self.control()
            self.update()
            self.draw()
            pygame.display.update()
            print("世代：", self.generation_num, "存活数量：", len(self.cakeManager.cakes), "分数：", self.score)
            if self.score >= 5000 and not os.path.exists("my_modle.csv"):
                data = self.cakeManager.cakes[0].neural_netwoek.getNetwork()
                weight_array = np.array(data['weights'])
                np.savetxt("./res/my_modle.csv", weight_array, delimiter=',')
                break
            # 计算游戏时间
            self.time += self.clock.tick(self.FPS)

    '''
    stop
    '''

    def stop(self):
        self.gameRunning = False

    '''
    restart
    '''

    def restart(self):
        self.game_init()


if __name__ == "__main__":
    game = Game()
    game.start()